<?php
namespace payment;

class Alipay
{
    private $merchant_private_key = '';
    private $alipay_public_key    = '';
    public $apiurl                = 'https://openapi.alipay.com/gateway.do';
    private $config               = [
        'app_id'         => '',
        'method'         => '',
        'format'         => 'JSON',
        'charset'        => 'utf-8',
        'sign_type'      => 'RSA2',
        'timestamp'      => '',
        'version'        => '1.0',
        'return_url'     => '',
        'notify_url'     => '',
        'app_auth_token' => '',
        'biz_content'    => '',
    ];
    private $order = [
        'trade_no'             => '',
        'out_trade_no'         => '',
        'product_code'         => '',
        'total_amount'         => '',
        'subject'              => '',
        'body'                 => '',
        'goods_detail'         => '',
        'passback_params'      => '',
        'extend_params'        => '',
        'goods_type'           => '',
        'timeout_express'      => '',
        'enable_pay_channels'  => '',
        'disable_pay_channels' => '',
        'auth_token'           => '',
        'qr_pay_mode'          => '',
        'qrcode_width'         => '',
        'refund_amount'        => '',
        'refund_reason'        => '',
        'out_request_no'       => '',
        'operator_id'          => '',
        'store_id'             => '',
        'terminal_id'          => '',
        'goods_detail'         => '',

        //转账
        'out_biz_no'           => '',
        'payee_type'           => '',
        'payee_account'        => '',
        'amount'               => '',
        'payer_show_name'      => '',
        'payee_real_name'      => '',
        'remark'               => '',
    ];

    public function __construct()
    {
        global $_G;
        $this->merchant_private_key = $_G['setting']['alipay_merchant_private_key'];
        $this->alipay_public_key    = $_G['setting']['alipay_alipay_public_key'];
        $this->config['app_id']     = $_G['setting']['alipay_app_id'];
        $this->config['timestamp']  = date('Y-m-d H:i:s');
    }

    private function buildRequestForm($qrcode = false)
    {
        if ($qrcode) {
            $this->order['qr_pay_mode']  = 4;
            $this->order['qrcode_width'] = 300;
        }
        $this->config['biz_content'] = json_encode(array_filter($this->order), JSON_UNESCAPED_UNICODE);
        $this->config['sign']        = $this->sign(array_filter($this->config));
        $html                        = '<form id="payform" action="' . $this->apiurl . '?charset=' . strtolower($this->config['charset']) . '" method="post" style="display:none;">';
        foreach ($this->config as $key => $value) {
            $html .= '<textarea name="' . $key . '">' . $value . '</textarea>';
        }
        $html .= '<button type="submit">submit</button>';
        $html .= '</form>';
        $html .= '<script>document.getElementById(\'payform\').submit();</script>';
        return $html;
    }

    private function curlPost()
    {
        $this->config['biz_content'] = json_encode(array_filter($this->order), JSON_UNESCAPED_UNICODE);
        $this->config['sign']        = $this->sign(array_filter($this->config));
        $ch                          = curl_init();
        curl_setopt($ch, CURLOPT_URL, $this->apiurl);
        curl_setopt($ch, CURLOPT_HEADER, false);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
        curl_setopt($ch, CURLOPT_TIMEOUT, 10);
        curl_setopt($ch, CURLOPT_POST, 1);
        curl_setopt($ch, CURLOPT_POSTFIELDS, array_filter(array_unique($this->config)));
        $reponse = curl_exec($ch);
        curl_close($ch);
        $result = json_decode($reponse, true);
        if (empty($result)) {
            $reponse = iconv('GBK', 'UTF-8', $reponse);
            $result  = json_decode($reponse, true);
        }
        return $result;
    }

    private function sign($param = [])
    {
        ksort($param);
        $str = '';
        foreach ($param as $k => $v) {
            $str .= ($str ? '&' : '') . $k . '=' . $v;
        }
        $res = "-----BEGIN RSA PRIVATE KEY-----" . PHP_EOL;
        $res .= wordwrap($this->merchant_private_key, 64, PHP_EOL, true);
        $res .= PHP_EOL . "-----END RSA PRIVATE KEY-----";
        openssl_sign($str, $sign, $res, OPENSSL_ALGO_SHA256);
        return base64_encode($sign);
    }

    public function verify($data, $sign)
    {
        if (isset($data['sign'])) {
            unset($data['sign']);
        }
        if (isset($data['sign_type'])) {
            unset($data['sign_type']);
        }
        ksort($data);
        $str = '';
        foreach ($data as $k => $v) {
            $str .= ($str ? '&' : '') . $k . '=' . $v;
        }
        $res = "-----BEGIN PUBLIC KEY-----\n" .
        wordwrap($this->alipay_public_key, 64, "\n", true) .
            "\n-----END PUBLIC KEY-----";
        return (bool) openssl_verify($str, base64_decode($sign), $res, OPENSSL_ALGO_SHA256);
    }

    public function setConfig($config)
    {
        $this->config = array_merge($this->config, $config);
        return $this;
    }

    public function setOrder($param)
    {
        foreach ($param as $k => $v) {
            switch ($k) {
                case 'order_id':
                    $this->order['out_trade_no'] = $v;
                    break;
                case 'price':
                    $this->order['total_amount'] = (string) $v;
                    break;

                default:
                    if (isset($this->order[$k])) {
                        $this->order[$k] = $v;
                    }
                    break;
            }
        }
        return $this;
    }

    public function unifiedOrder($type = 'form')
    {
        if (in_array($type, ['form', 'form-qrcode'])) {
            $this->order['product_code'] = 'FAST_INSTANT_TRADE_PAY';
            $this->config['method']      = 'alipay.trade.page.pay';
            return $this->buildRequestForm($type === 'form-qrcode');
        } else {
            $this->config['method'] = 'alipay.trade.precreate';
            return $this->curlPost();
        }
    }

    public function queryOrder()
    {
        $this->config['method'] = 'alipay.trade.query';
        $result                 = $this->curlPost();
        if (!empty($result['alipay_trade_query_response']['trade_status']) && in_array($result['alipay_trade_query_response']['trade_status'], ['TRADE_SUCCESS', 'TRADE_FINISHED'])) {
            $result['transaction_id'] = $result['alipay_trade_query_response']['trade_no'];
            $result['pay_time']       = $result['alipay_trade_query_response']['send_pay_date'];
        }
        return $result;
    }

    public function closeOrder()
    {
        $this->config['method'] = 'alipay.trade.close';
        return $this->curlPost();
    }

    public function refundOrder()
    {
        $this->config['method'] = 'alipay.trade.refund';
        return $this->curlPost();
    }

    public function queryRefundOrder()
    {
        $this->config['method'] = 'alipay.trade.fastpay.refund.query';
        return $this->curlPost();
    }

    public function transfer()
    {
        $this->config['method'] = 'alipay.fund.trans.toaccount.transfer';
        return $this->curlPost();
    }
}
